package platform.resource;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import framework.sepal4java.common.socket.AbstractTCP;
import framework.sepal4java.common.socket.CmdMessage;
import framework.sepal4java.common.socket.CmdMessageGrpIds;
import framework.sepal4java.common.socket.TCP;
import framework.sepal4java.common.struct.ConstantEnum.ResourceType;
import framework.sepal4java.common.struct.ResourceConfig;
import platform.pubsub.AbstractSubscriber;
import platform.pubsub.Channel;
import platform.pubsub.Publisher;

import java.io.IOException;
import java.net.Socket;

public class ResourceDriver extends AbstractSubscriber implements Runnable {
    private Thread t;
    private TCP tcp;
    private SensorMgr sensorMgr = null;
    private ActorMgr actorMgr = null;
    private ResourceType resourceType;
    private String deviceName;

    public ResourceDriver(Socket socket) {
        this.tcp = new DeviceDriverTCP(socket, false);
    }

    public class DeviceDriverTCP extends AbstractTCP {
        public DeviceDriverTCP(Socket socket, boolean lockFlag) {
            super(socket, lockFlag);
        }

        public DeviceDriverTCP(Socket socket) {
            super(socket);
        }

        @Override
        public void close() {
            try {
                socket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
//            disConnectPlatform();
        }

        @Override
        public void callback() {
            //TODO
            logger.error(String.format("[%s]: TCP connection is broken. Set the status to off", deviceName));
            if (resourceType == ResourceType.SENSOR) {
                unsubscribe(Channel.getSensorRequest(deviceName));
                sensorMgr.setAlive(false);
            } else if (resourceType == ResourceType.ACTOR) {
                unsubscribe(Channel.getActorRequest(deviceName));
                actorMgr.setAlive(false);
            } else if (resourceType == ResourceType.HYBRID) {
                sensorMgr.setAlive(false);
                actorMgr.setAlive(false);
                unsubscribe(Channel.getSensorRequest(deviceName));
                unsubscribe(Channel.getActorRequest(deviceName));
            }
        }
    }


    @Override
    public void run() {
        String msgFromClient;
        if ((msgFromClient = tcp.recv()) != null) {
            CmdMessage cmdMessage = new CmdMessage(msgFromClient);
            logger.info(String.format("[Wrapper -> Platform]: %s", cmdMessage));
            JSONObject joo = JSON.parseObject(cmdMessage.message);

            if (cmdMessage.cmd.equalsIgnoreCase("register")) {
                ResourceConfig resourceConfig = ResourceConfig.fromJSONObject(joo);
                resourceType = resourceConfig.type;
                deviceName = resourceConfig.name;
                if (resourceType == ResourceType.SENSOR) {
                    if (ResMgrThread.getSensorMgrs().containsKey(deviceName)) {
                        sensorMgr = ResMgrThread.getSensorMgrs().get(deviceName);
                        sensorMgr.setAlive(true);
                    } else {
                        sensorMgr = new SensorMgr(joo);
                        ResMgrThread.getSensorMgrs().put(deviceName, sensorMgr);
                    }
                    subscribe(Channel.getSensorRequest(deviceName));
                } else if (resourceType == ResourceType.ACTOR) {
                    if (ResMgrThread.getActorMgrs().containsKey(deviceName)) {
                        actorMgr = ResMgrThread.getActorMgrs().get(deviceName);
                        actorMgr.setAlive(true);
                    } else {
                        actorMgr = new ActorMgr(joo);
                        ResMgrThread.getActorMgrs().put(deviceName, actorMgr);
                    }
                    subscribe(Channel.getActorRequest(deviceName));
                } else if (resourceType == ResourceType.HYBRID) {
                    if (ResMgrThread.getSensorMgrs().containsKey(deviceName)) {
                        sensorMgr = ResMgrThread.getSensorMgrs().get(deviceName);
                        sensorMgr.setAlive(true);
                    } else {
                        sensorMgr = new SensorMgr(joo);
                        ResMgrThread.getSensorMgrs().put(deviceName, sensorMgr);
                    }
                    if (ResMgrThread.getActorMgrs().containsKey(deviceName)) {
                        actorMgr = ResMgrThread.getActorMgrs().get(deviceName);
                        actorMgr.setAlive(true);
                    } else {
                        actorMgr = new ActorMgr(joo);
                        ResMgrThread.getActorMgrs().put(deviceName, actorMgr);
                    }
                    subscribe(Channel.getSensorRequest(deviceName));
                    subscribe(Channel.getActorRequest(deviceName));
                }
            }

            CmdMessage ret = new CmdMessage("register_back", "true");
            tcp.send(ret.toString());
            logger.info(String.format("[Platform -> %s]: %s", deviceName, ret));
            tcp.setLockFlag(true);

            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if ((resourceType == ResourceType.SENSOR && !sensorMgr.isAlive()) ||
                        (resourceType == ResourceType.ACTOR && !actorMgr.isAlive()) ||
                        (resourceType == ResourceType.HYBRID && (!sensorMgr.isAlive() || !actorMgr.isAlive()))) {
                    break;
                }
                CmdMessage alive = new CmdMessage("alive_request", null);
                if (!tcp.send(alive.toString())) {
                    break;
                }
                tcp.unlock();
            }
        }
    }

    public void start() {
        if (t == null) {
            t = new Thread(this, getClass().getName());
            t.start();
        }
    }

    @Override
    public void onMessage(String channel, String msg) {
        if (resourceType == ResourceType.SENSOR && sensorMgr.isAlive() ||
        resourceType == ResourceType.ACTOR && actorMgr.isAlive() ||
        resourceType == ResourceType.HYBRID && sensorMgr.isAlive() && actorMgr.isAlive()) {
            CmdMessageGrpIds cmdMessageGrpIds = new CmdMessageGrpIds(msg);
            CmdMessage send = cmdMessageGrpIds.getCmdMessage();
            tcp.send(send.toString());
            logger.info(String.format("[Platform -> %s]: %s", deviceName + "<" + resourceType + ">", send));
            String ret = tcp.recv();
            CmdMessage recv;
            if (ret != null) {
                recv = new CmdMessage(ret);
            } else {
                if (send.cmd.equalsIgnoreCase("sensory_request")) {
                    recv = new CmdMessage("sensory_back", "@#$%");
                } else {
                    recv = new CmdMessage("action_back", "@#$%");
                }
            }
            logger.info(String.format("[%s -> Platform]: %s", deviceName + "<" + resourceType + ">", recv));
            for (Integer grpId : cmdMessageGrpIds.grpIds) {
                Publisher.publish(deviceName + "<" + resourceType + ">", grpId, recv.message);
            }
        }
    }
}
